// synch.cc
//      Routines for synchronizing threads.  Three kinds of
//      synchronization routines are defined here: semaphores, locks
//      and condition variables (the implementation of the last two
//      are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"

//----------------------------------------------------------------------
// Semaphore::Semaphore
//      Initialize a semaphore, so that it can be used for synchronization.
//
//      "debugName" is an arbitrary name, useful for debugging.
//      "initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore(char* debugName, int initialValue) {
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
//      De-allocate semaphore, when no longer needed.  Assume no one
//      is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore() {
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
//      Wait until semaphore value > 0, then decrement.  Checking the
//      value and decrementing must be done atomically, so we
//      need to disable interrupts before checking the value.
//
//      Note that Thread::Sleep assumes that interrupts are disabled
//      when it is called.
//----------------------------------------------------------------------

void
Semaphore::P() {
    IntStatus oldLevel = interrupt->SetLevel(IntOff); // disable interrupts

    while (value == 0) { // semaphore not available
        queue->Append((void *) currentThread); // so go to sleep
        currentThread->Sleep();
    }
    value--; // semaphore available,
    // consume its value

    (void) interrupt->SetLevel(oldLevel); // re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
//      Increment semaphore value, waking up a waiter if necessary.
//      As with P(), this operation must be atomic, so we need to disable
//      interrupts.  Scheduler::ReadyToRun() assumes that threads
//      are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V() {
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);

    thread = (Thread *) queue->Remove();
    if (thread != NULL) // make thread ready, consuming the V immediately
        scheduler->ReadyToRun(thread);
    value++;
    (void) interrupt->SetLevel(oldLevel);
}

// Dummy functions -- so we can compile our later assignments
// Note -- without a correct implementation of Condition::Wait(),
// the test case in the network assignment won't work!

Lock::Lock(char* debugName) {
    name = debugName;
    lockState = Lock::LOCK_STATE_FREE;
    lockOwner = NULL;
    waitQueue = new List;
}

Lock::~Lock() {
    if (lockOwner != NULL) {
        free(lockOwner); // Free the lockOwner if not NULL
    }
    free(waitQueue);
}

void Lock::Acquire() {
    IntStatus interruptStatus = interrupt->SetLevel(IntOff); //Disable interrupts

    if (isHeldByCurrentThread()) { // if currentthread is the owner of the thread.
        DEBUG('t', "INFO: %s tried to aquire a lock again.\n", getName());
    } else {
        if (lockState == Lock::LOCK_STATE_FREE) { // if the lock is free
            lockOwner = currentThread; // set currentThread as the owner
            lockState = Lock::LOCK_STATE_BUSY; // mark the lock state busy.
            DEBUG('t', "LOCK: %s is the lock owner.\n", getName());
        } else {
            DEBUG('t', "LOCK: Appending %s to lock's wait queue.\n", getName());
            waitQueue->Append((void *) currentThread); // lock is not available, so go to wait queue.
            currentThread->Sleep();
        }
    }

    interrupt->SetLevel(interruptStatus); // restoring the interrupts back.
}

void Lock::Release() {
    IntStatus interruptStatus = interrupt->SetLevel(IntOff); //Disable interrupts

    if (! isHeldByCurrentThread()) { // if currentthread is NOT the owner of the thread.
        DEBUG('t', "ERROR: %s tried to released the lock ownedby other thread.\n", currentThread->getName() );
    } else {
        Thread* nextThread = (Thread*) waitQueue->Remove();
        if (nextThread == NULL) { // if NO thread is waiting for lock, free the lock
            lockState = Lock::LOCK_STATE_FREE; //               and release ownership
            lockOwner = NULL;
            DEBUG('t', "LOCK: No threads waiting for lock - %s.\n", getName() );
        } else {
            // Wake up the thread and make him lock-owner
            DEBUG('t', "LOCK: Lock-%s woke up the next sleeping thread %s.\n", nextThread ->getName() );
            scheduler->ReadyToRun(nextThread);
            lockOwner = nextThread;
        }
    }

    interrupt->SetLevel(interruptStatus); // restoring the interrupts back.
}

//----------------------------------------------------------------------
// Lock::isHeldByCurrentThread()
//  Returns true if cureentThread is the owner of the lock
//----------------------------------------------------------------------

bool Lock::isHeldByCurrentThread() {
    return (currentThread == lockOwner  );
}

Condition::Condition(char* debugName) {
    condStatus = Condition::CONDITION_STATE_FREE ;
    name = debugName;
    waitqueue = new List;
    waitingLock = NULL;
}

Condition::~Condition() {
    delete (waitqueue);
}

void Condition::Wait(Lock* conditionLock) {

    IntStatus oldLevel = interrupt->SetLevel(IntOff); // Disabled the interrupts

    if (waitingLock == NULL) {
        condStatus = Condition::CONDITION_STATE_BUSY ;
        waitingLock = conditionLock;
    }

    if (waitingLock == conditionLock) {

        waitqueue->Append((void *) currentThread); //Adding Myself to the Wait Queue

        conditionLock -> Release(); // Exiting the Monitor
        currentThread -> Sleep(); //Putting Myslef to sleep
        conditionLock -> Acquire(); // Reentering the monitor
     }else {
            DEBUG('t', "INFO: %s Waited on different lock.", getName());
     }

    (void) interrupt->SetLevel(oldLevel);

}

void Condition::Signal(Lock* conditionLock) {

    Thread* thread = NULL;
    IntStatus oldLevel = interrupt->SetLevel(IntOff); // Disabled the interrupts

    thread = (Thread *) waitqueue->Remove();


    if (thread != NULL)
    {
        if (waitingLock != conditionLock) {
            DEBUG('t', "Error: %s Signaled on different lock.", getName());
        } else {
            scheduler->ReadyToRun(thread);
        }

    }else {
        waitingLock = NULL;
        condStatus = Condition::CONDITION_STATE_FREE ;
    }

    (void) interrupt->SetLevel(oldLevel);
}

void Condition::Broadcast(Lock* conditionLock)
 {
    while (! waitqueue->IsEmpty()) {
        Signal(conditionLock);
    }
    condStatus = Condition::CONDITION_STATE_FREE ;
}
